package com.base.custom

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin


/**
 * 手机上已安装的应用图标移动到指定中心点消失动画
 */
class IconAnimView(context: Context, attrs: AttributeSet?, defStyleAttr: Int): View(context, attrs,defStyleAttr){
    constructor(context: Context): this(context,null)
    constructor(context: Context, attrs: AttributeSet?): this(context,attrs,0)

    private val allDrawable by lazy {
        val manager = context.packageManager
        val list = manager.getInstalledPackages(0)
        val all =  mutableListOf<Drawable>()
        if (list.isEmpty()) {
            for (id in 0..100) {
                try {
                    val array = manager.getPackagesForUid(10000 + id)
                    if (!array.isNullOrEmpty()) {
                        for (name in array) {
                            if(name.contains(context.packageName))break
                            val info = manager.getPackageInfo(name, 0)
                            all.add(manager.getApplicationIcon(info.packageName))
                        }
                    }
                } catch (e: Exception) {}
            }
        }else{
            for (info in list) {
                if(!info.packageName.contains(context.packageName) && (info.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0){
                    all.add(manager.getApplicationIcon(info.packageName))
                }
            }
        }
        all
    }

    init { allDrawable }


    private val runItem = mutableListOf<Icon>()
    private val center = PointF()
    private val iconSize: Float by lazy { resources.displayMetrics.density * 50 }
    private val maxCount = 5
    private val animTime = 900L
    private var locationPoint: PointF? = null

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        center.set(w/2f,h/2f)
        if(w == 0 || h == 0)return

        setLocationPoint(locationPoint)
    }


    override fun draw(canvas: Canvas?) {
        super.draw(canvas)
        canvas ?: return
        for(icon in runItem){
            icon.draw(canvas)
        }
    }

    /**
     * 指定动画中心点
     */
    fun setLocationPoint(view: View){
        val array = IntArray(2)
        view.getLocationInWindow(array)
        setLocationPoint(PointF(array[0] + view.width/2f,array[1] + view.height/2f))
    }

    /**
     * 指定中动画心点
     */
    fun setLocationPoint(point: PointF? = null){
        this.locationPoint = point
        if (width == 0 || height == 0)return
        center.set(width/2f,height/2f)
        var radius = width.coerceAtLeast(height)/2f

        if(point != null){
            val array = IntArray(2)
            getLocationInWindow(array)
            val p = PointF(array[0] + width/2f,array[1] + height/2f)
           center.set(width/2f + point.x - p.x,height/2f + point.y - p.y)
            val width = resources.displayMetrics.widthPixels/2f
            val height = resources.displayMetrics.heightPixels/2f
            val w = width + abs(point.x - width)
            val h = height + abs(point.y - height)
            radius  = w.coerceAtLeast(h)
        }

        val count = maxCount.coerceAtMost(allDrawable.size)
        for(index in 0 until count){
            if(runItem.size <= index){ runItem.add(Icon()) }
            runItem[index].set(radius,iconSize.toInt())
        }
    }

    private val anim by lazy {
        val anim  = ValueAnimator.ofFloat(0f,1f)
        anim.interpolator = LinearInterpolator()
        anim.duration = 2500
        anim.repeatCount = ValueAnimator.INFINITE
        anim.addUpdateListener { postInvalidate() }
        anim
    }

    fun startAnim(){
        if(!anim.isRunning){
            anim.start()
            for(index in runItem.indices){
                runItem[index].start(index * animTime / maxCount)
            }
        }
        visibility = VISIBLE
    }

    fun stopAnim(){
        anim.cancel()
        runItem.forEach { it.end() }
        visibility = INVISIBLE
    }

    inner class Icon(){
        private val paint by lazy { val p = Paint();p.isAntiAlias = true;p }
        private val point = PointF()
        private val rect = RectF()
        private var drawable: Drawable? = null
        private var bitmap: Bitmap ?= null
        private var radius = 0f
        private var angle = 0f
        private var size = 0

        fun set(radius: Float,size: Int){
            this.radius = radius
            this.size = size
        }

        private val task = Runnable {
            anim.start()
        }

        fun start(offset: Long){
            removeCallbacks(task)
            postDelayed(task,offset)
        }

        fun end(){
            removeCallbacks(task)
            anim.cancel()
        }

        private val anim by lazy {
            val anim  = ValueAnimator.ofFloat(1f,0f)
            anim.interpolator = LinearInterpolator()
            anim.duration = animTime
            anim.repeatCount = ValueAnimator.INFINITE
            anim.addListener(object: AnimatorListenerAdapter(){
                override fun onAnimationStart(animation: Animator?) {
                    onRepeat()
                }
                override fun onAnimationRepeat(animation: Animator?) {
                    onRepeat()
                }
            })

            anim.addUpdateListener {
                val ratio = it.animatedValue as Float
                paint.alpha = (ratio * 255).toInt()
                point.set((center.x + radius * cos(angle * PI /180) * ratio).toFloat(),(center.y + radius * sin(angle * PI /180) * ratio).toFloat())
                rect.set(point.x - size/2f,point.y - size/2f,point.x + size/2f,point.y + size/2f)
            }
            anim
        }

        private fun onRepeat(){
            var angle: Float
            var run: Boolean
            var drawable: Drawable?
            do {
                angle = (0..360).random().toFloat()
                run = false
                for (item in runItem){
                    if(angle == item.angle){
                        run = true
                        break
                    }
                }
            }while (run)
            this.angle = angle
            do {
                run = false
                drawable = if (allDrawable.isEmpty()){
                    null
                }else{
                    allDrawable.getOrNull((0 until allDrawable.size).random())
                }
                for (item in runItem){
                    if (drawable == item.drawable){
                        run = true
                        break
                    }
                }
            }while (drawable != null && run)
            this.drawable = drawable
            this.angle = angle
            this.bitmap?.recycle()
            if (drawable != null){
                val bitmap  = Bitmap.createBitmap(size,size,if(drawable.opacity != PixelFormat.OPAQUE) Bitmap.Config.ARGB_8888 else Bitmap.Config.RGB_565)
                drawable.setBounds(0,0,size,size)
                drawable.draw(Canvas(bitmap))
                this.bitmap = bitmap
            }else{
                this.bitmap = null
            }
        }

        fun draw(canvas: Canvas){
            if (this.bitmap != null && rect.width() > 0 && rect.height() > 0){
                canvas.drawBitmap(bitmap!!,null,rect,paint)
            }
        }

    }
}